package cn.elwy.rcp.ui.composite;

import java.util.Arrays;
import java.util.HashMap;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogBlockedHandler;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.wizard.ProgressMonitorPart;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;

import cn.elwy.rcp.ui.listener.CloseListenerAdapter;

/**
 * 所有自定义面板的父类
 * 
 * @author huangsq
 * @version 1.0, 2014-09-25
 * @since 1.0, 2014-09-25
 */
public class BaseComposite extends Composite {

  private static final int HORIZONTAL_DIALOG_UNIT_PER_CHAR = 4;
  private static final int VERTICAL_DIALOG_UNITS_PER_CHAR = 8;
  public static final String DLG_IMG_HELP = "dialog_help_image"; //$NON-NLS-1$

  public static IDialogBlockedHandler blockedHandler = new IDialogBlockedHandler() {
    public void clearBlocked() {
    }

    public void showBlocked(IProgressMonitor blocking, IStatus blockingStatus, String blockedName) {
    }

    public void showBlocked(Shell parentShell, IProgressMonitor blocking, IStatus blockingStatus, String blockedName) {
    }
  };

  protected boolean lockedUI = false;
  protected boolean useCustomProgressMonitorPart = true;
  protected ProgressMonitorPart progressMonitorPart;

  private HashMap<Integer, Button> buttons = new HashMap<Integer, Button>();
  private CloseListenerAdapter closeListenerAdapter;
  private FontMetrics fontMetrics;
  private boolean helpAvailable;
  private ToolItem fHelpButton;

  /**
   * Create the dialog.
   * 
   * @param parent
   * @param style
   */
  public BaseComposite(Composite parent, int style) {
    super(parent, style);
  }

  protected ProgressMonitorPart createProgressMonitorPart(Composite composite, GridLayout pmlayout) {
    useCustomProgressMonitorPart = false;
    progressMonitorPart = new ProgressMonitorPart(composite, pmlayout, true) {
      String currentTask = null;

      public void setBlocked(IStatus reason) {
        super.setBlocked(reason);
        if (!lockedUI) {
          getBlockedHandler().showBlocked(getShell(), this, reason, currentTask);
        }
      }

      public void clearBlocked() {
        super.clearBlocked();
        if (!lockedUI) {
          getBlockedHandler().clearBlocked();
        }
      }

      public void beginTask(String name, int totalWork) {
        super.beginTask(name, totalWork);
        currentTask = name;
      }

      public void setTaskName(String name) {
        super.setTaskName(name);
        currentTask = name;
      }

      public void subTask(String name) {
        super.subTask(name);
        // If we haven't got anything yet use this value for more
        // context
        if (currentTask == null) {
          currentTask = name;
        }
      }
    };

    GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
    progressMonitorPart.setLayoutData(gridData);
    applyDialogFont(progressMonitorPart);
    return progressMonitorPart;
  }

  protected void createButtonsForButtonBar(Composite parent) {
    createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
    createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
  }

  protected void createWizardButtonsForButtonBar(Composite parent) {
    createButton(parent, IDialogConstants.BACK_ID, IDialogConstants.BACK_LABEL, false);
    createButton(parent, IDialogConstants.NEXT_ID, IDialogConstants.NEXT_LABEL, true);
    createButton(parent, IDialogConstants.FINISH_ID, IDialogConstants.FINISH_LABEL, false);
    createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
  }

  protected Button createButton(Composite parent, int id, String label, boolean defaultButton) {
    // increment the number of columns in the button bar
    ((GridLayout) parent.getLayout()).numColumns++;
    Button button = new Button(parent, SWT.PUSH);
    button.setText(label);
    button.setFont(JFaceResources.getDialogFont());
    button.setData(id);
    button.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent event) {
        buttonPressed(((Integer) event.widget.getData()).intValue());
      }
    });
    if (defaultButton) {
      Shell shell = parent.getShell();
      if (shell != null) {
        shell.setDefaultButton(button);
      }
    }
    buttons.put(id, button);
    setButtonLayoutData(button);
    return button;
  }

  protected Control createHelpControl(Composite parent) {
    Image helpImage = JFaceResources.getImage(DLG_IMG_HELP);
    if (helpImage != null) {
      return createHelpImageButton(parent, helpImage);
    }
    return createHelpLink(parent);
  }

  private Link createHelpLink(Composite parent) {
    Link link = new Link(parent, SWT.WRAP | SWT.NO_FOCUS);
    ((GridLayout) parent.getLayout()).numColumns++;
    link.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER));
    link.setText("<a>" + IDialogConstants.HELP_LABEL + "</a>"); //$NON-NLS-1$ //$NON-NLS-2$
    link.setToolTipText(IDialogConstants.HELP_LABEL);
    link.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        helpPressed();
      }
    });
    return link;
  }

  private ToolBar createHelpImageButton(Composite parent, Image image) {
    ToolBar toolBar = new ToolBar(parent, SWT.FLAT | SWT.NO_FOCUS);
    ((GridLayout) parent.getLayout()).numColumns++;
    toolBar.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER));
    final Cursor cursor = new Cursor(parent.getDisplay(), SWT.CURSOR_HAND);
    toolBar.setCursor(cursor);
    toolBar.addDisposeListener(new DisposeListener() {
      public void widgetDisposed(DisposeEvent e) {
        cursor.dispose();
      }
    });
    fHelpButton = new ToolItem(toolBar, SWT.CHECK);
    fHelpButton.setImage(image);
    fHelpButton.setToolTipText(JFaceResources.getString("helpToolTip")); //$NON-NLS-1$
    fHelpButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        helpPressed();
      }
    });
    return toolBar;
  }

  protected void setButtonLayoutData(Button button) {
    GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
    int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
    Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
    data.widthHint = Math.max(widthHint, minSize.x);
    button.setLayoutData(data);
  }

  protected void buttonPressed(int buttonId) {
    switch (buttonId) {
    case IDialogConstants.OK_ID:
      okPressed();
      break;
    case IDialogConstants.NEXT_ID:
      nextPressed();
      break;
    case IDialogConstants.BACK_ID:
      backPressed();
      break;
    case IDialogConstants.FINISH_ID:
      finishPressed();
      break;
    case IDialogConstants.CLOSE_ID:
      closePressed();
      break;
    case IDialogConstants.CANCEL_ID:
      cancelPressed();
      break;

    default:
      break;
    }
  }

  public boolean backPressed() {
    return true;
  }

  public boolean nextPressed() {
    return true;
  }

  public boolean finishPressed() {
    return true;
  }

  public boolean cancelPressed() {
    return closePressed();
  }

  public boolean closePressed() {
    if (closeListenerAdapter != null) {
      return closeListenerAdapter.close();
    }
    return false;
  }

  public boolean okPressed() {
    return true;
  }

  public boolean helpPressed() {
    return true;
  }

  public static int convertVerticalDLUsToPixels(FontMetrics fontMetrics, int dlus) {
    return (fontMetrics.getHeight() * dlus + VERTICAL_DIALOG_UNITS_PER_CHAR / 2) / VERTICAL_DIALOG_UNITS_PER_CHAR;
  }

  protected int convertVerticalDLUsToPixels(int dlus) {
    if (fontMetrics == null) {
      return 0;
    }
    return convertVerticalDLUsToPixels(fontMetrics, dlus);
  }

  public static int convertHorizontalDLUsToPixels(FontMetrics fontMetrics, int dlus) {
    return (fontMetrics.getAverageCharWidth() * dlus + HORIZONTAL_DIALOG_UNIT_PER_CHAR / 2)
        / HORIZONTAL_DIALOG_UNIT_PER_CHAR;
  }

  protected int convertHorizontalDLUsToPixels(int dlus) {
    if (fontMetrics == null) {
      return 0;
    }
    return convertHorizontalDLUsToPixels(fontMetrics, dlus);
  }

  protected void initializeDialogUnits(Control control) {
    GC gc = new GC(control);
    gc.setFont(JFaceResources.getDialogFont());
    fontMetrics = gc.getFontMetrics();
    gc.dispose();
  }

  public static void applyDialogFont(Control control) {
    if (control == null || dialogFontIsDefault()) {
      return;
    }
    Font dialogFont = JFaceResources.getDialogFont();
    applyDialogFont(control, dialogFont);
  }

  protected static boolean dialogFontIsDefault() {
    FontData[] dialogFontData = JFaceResources.getFontRegistry().getFontData(JFaceResources.DIALOG_FONT);
    FontData[] defaultFontData = JFaceResources.getFontRegistry().getFontData(JFaceResources.DEFAULT_FONT);
    return Arrays.equals(dialogFontData, defaultFontData);
  }

  private static void applyDialogFont(Control control, Font dialogFont) {
    if (hasDefaultFont(control)) {
      control.setFont(dialogFont);
    }
    if (control instanceof Composite) {
      Control[] children = ((Composite) control).getChildren();
      for (int i = 0; i < children.length; i++) {
        applyDialogFont(children[i], dialogFont);
      }
    }
  }

  private static boolean hasDefaultFont(Control control) {
    FontData[] controlFontData = control.getFont().getFontData();
    FontData[] defaultFontData = getDefaultFont(control).getFontData();
    if (controlFontData.length == defaultFontData.length) {
      for (int i = 0; i < controlFontData.length; i++) {
        if (controlFontData[i].equals(defaultFontData[i])) {
          continue;
        }
        return false;
      }
      return true;
    }
    return false;
  }

  private static Font getDefaultFont(Control control) {
    String fontName = "DEFAULT_FONT_" + control.getClass().getName(); //$NON-NLS-1$
    if (JFaceResources.getFontRegistry().hasValueFor(fontName)) {
      return JFaceResources.getFontRegistry().get(fontName);
    }
    Font cached = control.getFont();
    control.setFont(null);
    Font defaultFont = control.getFont();
    control.setFont(cached);
    JFaceResources.getFontRegistry().put(fontName, defaultFont.getFontData());
    return defaultFont;
  }

  public IDialogBlockedHandler getBlockedHandler() {
    return blockedHandler;
  }

  public void setBlockedHandler(IDialogBlockedHandler blockedHandler) {
    Dialog.blockedHandler = blockedHandler;
  }

  protected Button getButton(int id) {
    return (Button) buttons.get(new Integer(id));
  }

  public boolean isHelpAvailable() {
    return helpAvailable;
  }

  public void setHelpAvailable(boolean helpAvailable) {
    this.helpAvailable = helpAvailable;
  }

  public FontMetrics getFontMetrics() {
    return fontMetrics;
  }

  public CloseListenerAdapter getCloseListenerAdapter() {
    return closeListenerAdapter;
  }

  public void setCloseListenerAdapter(CloseListenerAdapter closeListenerAdapter) {
    this.closeListenerAdapter = closeListenerAdapter;
  }

}
